#include <proc.h>
#include <elf.h>
#include "fs.h"

#ifdef __LP64__
# define Elf_Ehdr Elf64_Ehdr
# define Elf_Phdr Elf64_Phdr
#else
# define Elf_Ehdr Elf32_Ehdr
# define Elf_Phdr Elf32_Phdr
#endif

/* 读取文件到 buf */
static void read_file(int fd, void* buf, size_t offset, size_t len) {
    fs_lseek(fd, offset, SEEK_SET);
    fs_read(fd, buf, len);
}

/**
 * 加载程序其实就是把比特串放置在正确的位置
 * 目前来说, ramdisk 中由多个 elf 数据, 某个时刻最多只有一个 elf 被加载到 0x83000000 并展开 (nanos则是由objcpy展开再被载入到nemu中的)
 * 它也变为了一个程序, 拥有自己的 数据段、代码段、堆栈等, 这些在 AM 看来也不过是一些数据, 没有什么特别之处
 * */
static uintptr_t loader(PCB *pcb, const char *filename) {
    // 打开文件
    int fd = fs_open(filename, 0, 0);
    assert(fd >= 0);

    // 读取 ELF header 并验证合法性
    Elf_Ehdr ehdr;
    read_file(fd, &ehdr, 0, sizeof(ehdr));
    assert(*(uint32_t *)ehdr.e_ident == 0x464c457f);

    // 获取 program header table 相关信息
    size_t phnum = ehdr.e_phnum;
    size_t phoff = ehdr.e_phoff;
    size_t phentsize = ehdr.e_phentsize;

    // 读取 program header table
    Elf_Phdr phdr_tab[phnum];
    read_file(fd, phdr_tab, phoff, phentsize*phnum);

    // 读取 segment 到正确的位置
    for (size_t i = 0; i < phnum; ++i) {
        if  (phdr_tab[i].p_type != PT_LOAD) continue;
        read_file(fd, (void*)phdr_tab[i].p_vaddr, phdr_tab[i].p_offset, phdr_tab[i].p_filesz);
        size_t fill = phdr_tab[i].p_memsz - phdr_tab[i].p_filesz;
        if (fill <= 0) continue;
        memset((void*)(phdr_tab[i].p_vaddr + phdr_tab[i].p_filesz), 0, fill);
    }

    // 返回 elf 文件的入口地址
    return ehdr.e_entry;
}

void naive_uload(PCB *pcb, const char *filename) {
    uintptr_t entry = loader(pcb, filename);
    Log("Jump to entry = %p", entry);
    ((void(*)())entry) ();
}
